﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace LxBasic
{
    public struct Vector3d
    {
        public Vector3d(double x, double y, double z)
        {
            this.X = x;
            this.Y = y;
            this.Z = z;
        }
        public double X;
        public double Y;
        public double Z;
        public static Vector3d Zero => new Vector3d(0, 0, 0);
        public static Vector3d UnitX => new Vector3d(1, 0, 0);
        public static Vector3d UnitY => new Vector3d(0, 1, 0);
        public static Vector3d UnitZ => new Vector3d(0, 0, 1);
        public double this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                switch (index)
                {
                    case 0: return X;
                    case 1: return Y;
                    case 2: return Z;
                    default: throw new IndexOutOfRangeException();
                }
            }
        }
        public void Negate()
        {
            X = -X;
            Y = -Y;
            Z = -Z;
        }
        public void Normalize()
        {
            double l = Length();
            X /= l;
            Y /= l;
            Z /= l;
        }
        public double LengthSquared() => X * X + Y * Y + Z * Z;

        public double Length() => Math.Sqrt(LengthSquared());
        public bool Equals(Vector3d other)
        {
            return X == other.X && Y == other.Y && Z == other.Z;
        }
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            return obj is Vector3d v ? Equals(v) : false;
        }
        public override int GetHashCode()
        {
            return (X.GetHashCode() * 397) ^ (Y.GetHashCode() * 31) ^ Z.GetHashCode();
        }
        public override string ToString()
        {
            return string.Format($"[{X},{Y},{Z}]");
        }
        //public bool IsEqual0()
        //{
        //    return X == 0 && Y == 0 && Z == 0;
        //}
        //public static implicit operator Vector3d(Vector3D v)
        //{
        //    return new Vector3d(v.X, v.Y, v.Z);
        //}
        //public static implicit operator Vector3d(Point3D v)
        //{
        //    return new Vector3d(v.X, v.Y, v.Z);
        //}
        //public static explicit operator Point3D(Vector3d v)
        //{
        //    return new Point3D(v.X, v.Y, v.Z);
        //}
        //public static explicit operator Vector3D(Vector3d v)
        //{
        //    return new Vector3D(v.X, v.Y, v.Z);
        //}
        public static bool TryParse(string inputString, out Vector3d result)
        {
            string t3 = inputString.Replace("，", ",");
            t3 = t3.Trim();
            if (t3.StartsWith('[') && t3.EndsWith(']'))
            {
                t3 = t3.Substring(1, t3.Length - 2);
            }
            string[] s3 = t3.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            result = new Vector3d();
            if (s3.Length == 3)
            {
                if (double.TryParse(s3[0], out result.X) && double.TryParse(s3[1], out result.Y) && double.TryParse(s3[2], out result.Z))
                {
                    return true;
                }
            }
            return false;
        }
        public static Vector3d Parse(string inputString)
        {
            if (TryParse(inputString, out Vector3d result))
            {
                return result;
            }
            else
            {
                throw new Exception($"转换Vector3d出错 - {inputString}");
            }
        }
        public static bool operator ==(Vector3d a, Vector3d b)
        {
            return a.Equals(b);
        }
        public static bool operator !=(Vector3d a, Vector3d b)
        {
            return !a.Equals(b);
        }
        public static Vector3d operator +(Vector3d a, Vector3d b)
        {
            return new Vector3d(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
        }
        public static Vector3d operator -(Vector3d a, Vector3d b)
        {
            return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
        }
        public static Vector3d operator -(Vector3d a)
        {
            return new Vector3d(-a.X, -a.Y, -a.Z);
        }
        public static Vector3d operator *(double d, Vector3d v)
        {
            return new Vector3d(v.X * d, v.Y * d, v.Z * d);
        }
        public static Vector3d operator *(Vector3d v, double d)
        {
            return new Vector3d(v.X * d, v.Y * d, v.Z * d);
        }
        public static double DotProduct(in Vector3d a, in Vector3d b)
        {
            return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
        }
        public static Vector3d CrossProduct(in Vector3d a, in Vector3d b)
        {
            return new Vector3d(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
        }
        public static Vector3d ProjectPointToPlane(in Vector3d pointOut, in Vector3d planeNormal, in Vector3d planePoint)
        {
            double t = DotProduct(planeNormal, pointOut - planePoint);
            return pointOut - t / planeNormal.LengthSquared() * planeNormal;
        }
        public static Vector3d ProjectVectorToPlane(in Vector3d Vector, in Vector3d planeNormal)//矢量在平面上的投影
        {
            return Vector - DotProduct(Vector, planeNormal) / planeNormal.LengthSquared() * planeNormal;
        }
        public static (Vector3d u, Vector3d v) FindVerticalVector(in Vector3d vector)
        {
            if (vector == Zero) { return (Zero, Zero); }
            Vector3d u = (Math.Abs(vector.X) < Math.Abs(vector.Y)) ?
                     new Vector3d(0, vector.Z, -vector.Y) :
                     new Vector3d(vector.Z, 0, -vector.X);
            u.Normalize();
            Vector3d v = CrossProduct(vector, u);
            v.Normalize();
            return (u, v);
        }
    }
}
